/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <plat/device.h>
#include <plat/irqn.h>
#include <arch/driver/gic_v2.h>
#include <stdlib/assert.h>
#include <core/dprintf.h>

#define DEVICE_SIZE_GIC   (64 * 1024)
#define DEVICE_SIZE_RES   (4 * 1024)
#define DEVICE_SIZE_GICD  (4 * 1024)
#define DEVICE_SIZE_GICC  (4 * 1024)
#define DEVICE_VBASE_GICD (DEVICE_VBASE_GIC + DEVICE_SIZE_RES)
#define DEVICE_VBASE_GICC (DEVICE_VBASE_GICD + DEVICE_SIZE_GICD)

#define ERROR_DEPRECATED 0

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

#define ARM_IRQ_SEC_TMU_CH0 (168u)

#define INTR_TYPE_NS (2u)

#define SCR_IRQ_BIT (1 << 1)
#define SCR_FIQ_BIT (1 << 2)

#define MIN_SGI_ID     0
#define MIN_SEC_SGI_ID 8
#define MIN_PPI_ID     16
#define MIN_SPI_ID     32
#define MAX_SPI_ID     1019

#define TOTAL_SPI_INTR_NUM  (MAX_SPI_ID - MIN_SPI_ID + 1)
#define TOTAL_PCPU_INTR_NUM (MIN_SPI_ID - MIN_SGI_ID)

#define GIC_PRI_MASK 0xff

#define GIC_CFG_MASK 0x3

#define GIC_SPURIOUS_INTERRUPT 1023

#define GIC_INTR_CFG_LEVEL 0
#define GIC_INTR_CFG_EDGE  1

#define GIC_HIGHEST_SEC_PRIORITY 0x0
#define GIC_LOWEST_SEC_PRIORITY  0x7f
#define GIC_HIGHEST_NS_PRIORITY  0x80
#define GIC_LOWEST_NS_PRIORITY   0xfe /* 0xff would disable all interrupts */

#define GICD_CTLR       0x0
#define GICD_TYPER      0x4
#define GICD_IIDR       0x8
#define GICD_IGROUPR    0x80
#define GICD_ISENABLER  0x100
#define GICD_ICENABLER  0x180
#define GICD_ISPENDR    0x200
#define GICD_ICPENDR    0x280
#define GICD_ISACTIVER  0x300
#define GICD_ICACTIVER  0x380
#define GICD_IPRIORITYR 0x400
#define GICD_ICFGR      0xc00
#define GICD_NSACR      0xe00

#define CTLR_ENABLE_G0_SHIFT 0
#define CTLR_ENABLE_G0_MASK  0x1
#define CTLR_ENABLE_G0_BIT   (1 << CTLR_ENABLE_G0_SHIFT)

#define PIDR2_ARCH_REV_SHIFT 4
#define PIDR2_ARCH_REV_MASK  0xf

#define ARCH_REV_GICV3 0x3
#define ARCH_REV_GICV2 0x2
#define ARCH_REV_GICV1 0x1

#define IGROUPR_SHIFT    5
#define ISENABLER_SHIFT  5
#define ICENABLER_SHIFT  ISENABLER_SHIFT
#define ISPENDR_SHIFT    5
#define ICPENDR_SHIFT    ISPENDR_SHIFT
#define ISACTIVER_SHIFT  5
#define ICACTIVER_SHIFT  ISACTIVER_SHIFT
#define IPRIORITYR_SHIFT 2
#define ITARGETSR_SHIFT  2
#define ICFGR_SHIFT      4
#define NSACR_SHIFT      4

#define TYPER_IT_LINES_NO_SHIFT 0
#define TYPER_IT_LINES_NO_MASK  0x1f

#define GICD_IPRIORITYR_DEF_VAL                                                                   \
    (GIC_HIGHEST_NS_PRIORITY | (GIC_HIGHEST_NS_PRIORITY << 8) | (GIC_HIGHEST_NS_PRIORITY << 16) | \
     (GIC_HIGHEST_NS_PRIORITY << 24))

#define GICC_IAR_INTID_WIDTH 10

#define GICV2_INTR_GROUP0 0
#define GICV2_INTR_GROUP1 1

#define PENDING_G1_INTID 1022

#define GICV2_MAX_TARGET_PE 8

#define GICD_PIDR2_GICV2 0xFE8

#define SGIR_TGTLSTFLT_SHIFT 24
#define SGIR_TGTLSTFLT_MASK  0x3
#define SGIR_TGTLST_SHIFT    16
#define SGIR_TGTLST_MASK     0xff
#define SGIR_INTID_MASK      0xf

#define SGIR_TGT_SPECIFIC 0

#define GICV2_SGIR_VALUE(tgt_lst_flt, tgt, intid)                      \
    ((((tgt_lst_flt) & SGIR_TGTLSTFLT_MASK) << SGIR_TGTLSTFLT_SHIFT) | \
     (((tgt) & SGIR_TGTLST_MASK) << SGIR_TGTLST_SHIFT) | ((intid) & SGIR_INTID_MASK))

#define CTLR_ENABLE_G1_SHIFT 1
#define CTLR_ENABLE_G1_MASK  0x1
#define CTLR_ENABLE_G1_BIT   (1 << CTLR_ENABLE_G1_SHIFT)

#define GIC400_NUM_SPIS (480u)
#define MAX_PPIS        (14u)
#define MAX_SGIS        (16u)

#define GRP0                (0u)
#define GRP1                (1u)
#define GIC_TARGET_CPU_MASK (0xffu)

#define ENABLE_GRP0 ((1u) << 0)
#define ENABLE_GRP1 ((1u) << 1)

#define GICD_ITARGETSR (0x800u)
#define GICD_SGIR      (0xF00)
#define GICD_CPENDSGIR (0xF10)
#define GICD_SPENDSGIR (0xF20)

#define CPENDSGIR_SHIFT (2u)
#define SPENDSGIR_SHIFT CPENDSGIR_SHIFT

#define IT_LINES_NO_MASK (0x1fu)

#define ICDSGIR_BIT_TLF   (24u)
#define ICDSGIR_BIT_CPUTL (16u)
#define ICDSGIR_BIT_SATT  (15u)

#define GICC_CTLR     (0x0u)
#define GICC_PMR      (0x4u)
#define GICC_BPR      (0x8u)
#define GICC_IAR      (0xCu)
#define GICC_EOIR     (0x10u)
#define GICC_RPR      (0x14u)
#define GICC_HPPIR    (0x18u)
#define GICC_AHPPIR   (0x28u)
#define GICC_IIDR     (0xFCu)
#define GICC_DIR      (0x1000u)
#define GICC_PRIODROP GICC_EOIR

#define INT_ID_MASK (0x3ffu)

#define EOI_MODE_NS      ((1u) << 10)
#define EOI_MODE_S       ((1u) << 9)
#define IRQ_BYP_DIS_GRP1 ((1u) << 8)
#define FIQ_BYP_DIS_GRP1 ((1u) << 7)
#define IRQ_BYP_DIS_GRP0 ((1u) << 6)
#define FIQ_BYP_DIS_GRP0 ((1u) << 5)
#define CBPR             ((1u) << 4)
#define FIQ_EN           ((1u) << 3)
#define ACK_CTL          ((1u) << 2)
#define FIQ_EN_SHIFT     3
#define FIQ_EN_BIT       (1 << FIQ_EN_SHIFT)

#define GICC_IIDR_PID_SHIFT  (20u)
#define GICC_IIDR_ARCH_SHIFT (16u)
#define GICC_IIDR_REV_SHIFT  (12u)
#define GICC_IIDR_IMP_SHIFT  (0u)

#define GICC_IIDR_PID_MASK  (0xfffu)
#define GICC_IIDR_ARCH_MASK (0xfu)
#define GICC_IIDR_REV_MASK  (0xfu)
#define GICC_IIDR_IMP_MASK  (0xfffu)

#define GICH_CTL     (0x0u)
#define GICH_VTR     (0x4u)
#define GICH_ELRSR0  (0x30u)
#define GICH_ELRSR1  (0x34u)
#define GICH_APR0    (0xF0u)
#define GICH_LR_BASE (0x100u)

#define GICV_CTL         (0x0u)
#define GICV_PRIMASK     (0x4u)
#define GICV_BP          (0x8u)
#define GICV_INTACK      (0xCu)
#define GICV_EOI         (0x10u)
#define GICV_RUNNINGPRI  (0x14u)
#define GICV_HIGHESTPEND (0x18u)
#define GICV_DEACTIVATE  (0x1000u)

#define LEVEL_TRIGGER (0)
#define EDGE_TIRGGER  (1)

#define SECURITY     (1)
#define NON_SECURITY (0)

#define INTR_PROP_DESC(num, pri, grp, cfg)                                  \
    {                                                                       \
        .intr_num = num, .intr_pri = pri, .intr_grp = grp, .intr_cfg = cfg, \
    }

typedef struct interrupt_prop {
    unsigned int intr_num : 10;
    unsigned int intr_pri : 8;
    unsigned int intr_grp : 2;
    unsigned int intr_cfg : 2;
} interrupt_prop_t;

#ifndef uint32_t
typedef unsigned int uint32_t;
#endif

typedef struct gicv2_driver_data {
    unsigned long gicd_base;
    unsigned long gicc_base;
#if !ERROR_DEPRECATED
    unsigned int        g0_interrupt_num;
    const unsigned int *g0_interrupt_array;
#endif
    unsigned int           *target_masks;
    unsigned int            target_masks_num;
    const interrupt_prop_t *interrupt_props;
    unsigned int            interrupt_props_num;
} gicv2_driver_data_t;

static inline void mmio_write_8(unsigned long addr, uint8_t value)
{
    *(volatile uint8_t *)addr = value;
}

static inline uint8_t mmio_read_8(unsigned long addr)
{
    return *(volatile uint8_t *)addr;
}

static inline void mmio_write_16(unsigned long addr, uint16_t value)
{
    *(volatile uint16_t *)addr = value;
}

static inline uint16_t mmio_read_16(unsigned long addr)
{
    return *(volatile uint16_t *)addr;
}

static inline uint32_t mmio_read_32(unsigned long addr)
{
    return *(volatile uint32_t *)addr;
}

static inline void mmio_write_32(unsigned long addr, uint32_t value)
{
    *(volatile uint32_t *)addr = value;
}

static inline unsigned int gicd_read_ctlr(unsigned long base)
{
    return mmio_read_32(base + GICD_CTLR);
}

static inline unsigned int gicd_read_typer(unsigned long base)
{
    return mmio_read_32(base + GICD_TYPER);
}

static inline unsigned int gicd_read_sgir(unsigned long base)
{
    return mmio_read_32(base + GICD_SGIR);
}

static inline void gicd_write_ctlr(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICD_CTLR, val);
}

unsigned int gicd_read_nsacr(unsigned long base, unsigned int id)
{
    unsigned n = id >> NSACR_SHIFT;
    return mmio_read_32(base + GICD_NSACR + (n << 2));
}

void gicd_write_nsacr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> NSACR_SHIFT;
    mmio_write_32(base + GICD_NSACR + (n << 2), val);
}

unsigned int gicd_read_igroupr(unsigned long base, unsigned int id)
{
    unsigned n = id >> IGROUPR_SHIFT;
    return mmio_read_32(base + GICD_IGROUPR + (n << 2));
}

unsigned int gicd_read_isenabler(unsigned long base, unsigned int id)
{
    unsigned n = id >> ISENABLER_SHIFT;
    return mmio_read_32(base + GICD_ISENABLER + (n << 2));
}

unsigned int gicd_read_icenabler(unsigned long base, unsigned int id)
{
    unsigned n = id >> ICENABLER_SHIFT;
    return mmio_read_32(base + GICD_ICENABLER + (n << 2));
}

unsigned int gicd_read_ispendr(unsigned long base, unsigned int id)
{
    unsigned n = id >> ISPENDR_SHIFT;
    return mmio_read_32(base + GICD_ISPENDR + (n << 2));
}

unsigned int gicd_read_icpendr(unsigned long base, unsigned int id)
{
    unsigned n = id >> ICPENDR_SHIFT;
    return mmio_read_32(base + GICD_ICPENDR + (n << 2));
}

unsigned int gicd_read_isactiver(unsigned long base, unsigned int id)
{
    unsigned n = id >> ISACTIVER_SHIFT;
    return mmio_read_32(base + GICD_ISACTIVER + (n << 2));
}

unsigned int gicd_read_icactiver(unsigned long base, unsigned int id)
{
    unsigned n = id >> ICACTIVER_SHIFT;
    return mmio_read_32(base + GICD_ICACTIVER + (n << 2));
}

unsigned int gicd_read_ipriorityr(unsigned long base, unsigned int id)
{
    unsigned n = id >> IPRIORITYR_SHIFT;
    return mmio_read_32(base + GICD_IPRIORITYR + (n << 2));
}

unsigned int gicd_read_itargetsr(unsigned long base, unsigned int id)
{
    unsigned n = id >> ITARGETSR_SHIFT;
    return mmio_read_32(base + GICD_ITARGETSR + (n << 2));
}

unsigned int gicd_read_icfgr(unsigned long base, unsigned int id)
{
    unsigned n = id >> ICFGR_SHIFT;
    return mmio_read_32(base + GICD_ICFGR + (n << 2));
}

unsigned int gicd_read_cpendsgir(unsigned long base, unsigned int id)
{
    unsigned n = id >> CPENDSGIR_SHIFT;
    return mmio_read_32(base + GICD_CPENDSGIR + (n << 2));
}

unsigned int gicd_read_spendsgir(unsigned long base, unsigned int id)
{
    unsigned n = id >> SPENDSGIR_SHIFT;
    return mmio_read_32(base + GICD_SPENDSGIR + (n << 2));
}

void gicd_write_igroupr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> IGROUPR_SHIFT;
    mmio_write_32(base + GICD_IGROUPR + (n << 2), val);
}

void gicd_write_isenabler(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISENABLER_SHIFT;
    mmio_write_32(base + GICD_ISENABLER + (n << 2), val);
}

void gicd_write_icenabler(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICENABLER_SHIFT;
    mmio_write_32(base + GICD_ICENABLER + (n << 2), val);
}

void gicd_write_ispendr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISPENDR_SHIFT;
    mmio_write_32(base + GICD_ISPENDR + (n << 2), val);
}

void gicd_write_icpendr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICPENDR_SHIFT;
    mmio_write_32(base + GICD_ICPENDR + (n << 2), val);
}

void gicd_write_isactiver(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISACTIVER_SHIFT;
    mmio_write_32(base + GICD_ISACTIVER + (n << 2), val);
}

void gicd_write_icactiver(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICACTIVER_SHIFT;
    mmio_write_32(base + GICD_ICACTIVER + (n << 2), val);
}

void gicd_write_ipriorityr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> IPRIORITYR_SHIFT;
    mmio_write_32(base + GICD_IPRIORITYR + (n << 2), val);
}

void gicd_write_itargetsr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ITARGETSR_SHIFT;
    mmio_write_32(base + GICD_ITARGETSR + (n << 2), val);
}

void gicd_write_icfgr(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICFGR_SHIFT;
    mmio_write_32(base + GICD_ICFGR + (n << 2), val);
}

void gicd_write_cpendsgir(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> CPENDSGIR_SHIFT;
    mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val);
}

void gicd_write_spendsgir(unsigned long base, unsigned int id, unsigned int val)
{
    unsigned n = id >> SPENDSGIR_SHIFT;
    mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val);
}

static inline unsigned int gicd_read_pidr2(unsigned long base)
{
    return mmio_read_32(base + GICD_PIDR2_GICV2);
}

static inline unsigned int gicd_get_itargetsr(unsigned long base, unsigned int id)
{
    return mmio_read_8(base + GICD_ITARGETSR + id);
}

static inline void gicd_set_itargetsr(unsigned long base, unsigned int id, unsigned int target)
{
    mmio_write_8(base + GICD_ITARGETSR + id, (target & GIC_TARGET_CPU_MASK));
}

static inline void gicd_write_sgir(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICD_SGIR, val);
}

static inline unsigned int gicc_read_ctlr(unsigned long base)
{
    return mmio_read_32(base + GICC_CTLR);
}

static inline unsigned int gicc_read_pmr(unsigned long base)
{
    return mmio_read_32(base + GICC_PMR);
}

static inline unsigned int gicc_read_BPR(unsigned long base)
{
    return mmio_read_32(base + GICC_BPR);
}

static inline unsigned int gicc_read_IAR(unsigned long base)
{
    return mmio_read_32(base + GICC_IAR);
}

static inline unsigned int gicc_read_EOIR(unsigned long base)
{
    return mmio_read_32(base + GICC_EOIR);
}

static inline unsigned int gicc_read_hppir(unsigned long base)
{
    return mmio_read_32(base + GICC_HPPIR);
}

static inline unsigned int gicc_read_ahppir(unsigned long base)
{
    return mmio_read_32(base + GICC_AHPPIR);
}

static inline unsigned int gicc_read_dir(unsigned long base)
{
    return mmio_read_32(base + GICC_DIR);
}

static inline unsigned int gicc_read_iidr(unsigned long base)
{
    return mmio_read_32(base + GICC_IIDR);
}

static inline unsigned int gicc_read_rpr(unsigned long base)
{
    return mmio_read_32(base + GICC_RPR);
}

static inline void gicc_write_ctlr(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_CTLR, val);
}

static inline void gicc_write_pmr(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_PMR, val);
}

static inline void gicc_write_BPR(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_BPR, val);
}

static inline void gicc_write_IAR(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_IAR, val);
}

static inline void gicc_write_EOIR(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_EOIR, val);
}

static inline void gicc_write_hppir(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_HPPIR, val);
}

static inline void gicc_write_dir(unsigned long base, unsigned int val)
{
    mmio_write_32(base + GICC_DIR, val);
}

unsigned int gicv2_get_cpuif_id(unsigned long base)
{
    unsigned int val;

    val = gicd_read_itargetsr(base, 0);
    return val & GIC_TARGET_CPU_MASK;
}

unsigned int gicd_get_igroupr(unsigned long base, unsigned int id)
{
    unsigned     bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    return (reg_val >> bit_num) & 0x1;
}

void gicd_set_igroupr(unsigned long base, unsigned int id)
{
    unsigned     bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    gicd_write_igroupr(base, id, reg_val | (1 << bit_num));
}

void gicd_clr_igroupr(unsigned long base, unsigned int id)
{
    unsigned     bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    gicd_write_igroupr(base, id, reg_val & ~(1 << bit_num));
}

void gicd_set_isenabler(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISENABLER_SHIFT) - 1);

    gicd_write_isenabler(base, id, (1 << bit_num));
}

void gicd_set_icenabler(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1);

    gicd_write_icenabler(base, id, (1 << bit_num));
}

void gicd_set_ispendr(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1);

    gicd_write_ispendr(base, id, (1 << bit_num));
}

void gicd_set_icpendr(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1);

    gicd_write_icpendr(base, id, (1 << bit_num));
}

void gicd_set_isactiver(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);

    gicd_write_isactiver(base, id, (1 << bit_num));
}

void gicd_set_icactiver(unsigned long base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICACTIVER_SHIFT) - 1);

    gicd_write_icactiver(base, id, (1 << bit_num));
}

unsigned int gicd_get_isactiver(unsigned long base, unsigned int id)
{
    unsigned int bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);
    unsigned int reg_val = gicd_read_isactiver(base, id);

    return (reg_val >> bit_num) & 0x1;
}

void gicd_set_icfgr(unsigned long base, unsigned int id, unsigned int cfg)
{
    unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1);
    uint32_t reg_val = gicd_read_icfgr(base, id);

    reg_val &= ~(GIC_CFG_MASK << bit_num);
    reg_val |= ((cfg & GIC_CFG_MASK) << bit_num);

    gicd_write_icfgr(base, id, reg_val);
}

void gicd_set_ipriorityr(unsigned long base, unsigned int id, unsigned int pri)
{
    mmio_write_8(base + GICD_IPRIORITYR + id, pri & GIC_PRI_MASK);
}

uint32_t gicv2_interrupt_type_to_line(uint64_t cpuif_base, uint32_t type)
{
    uint32_t gicc_ctlr;

    if (type == INTR_TYPE_NS) {
        return __builtin_ctz(SCR_IRQ_BIT);
    }

    gicc_ctlr = gicc_read_ctlr(cpuif_base);
    if (gicc_ctlr & FIQ_EN) {
        return __builtin_ctz(SCR_FIQ_BIT);
    } else {
        return __builtin_ctz(SCR_IRQ_BIT);
    }
}

void gicv2_spis_configure_defaults(unsigned long gicd_base)
{
    unsigned int index, num_ints;

    num_ints = gicd_read_typer(gicd_base);
    num_ints &= TYPER_IT_LINES_NO_MASK;
    num_ints = (num_ints + 1) << 5;

    dprintf("the max surpport interrupt number num_ints = %d \n", num_ints);

    for (index = MIN_SPI_ID; index < num_ints; index += 32) {
        gicd_write_igroupr(gicd_base, index, ~0U);
    }

    for (index = MIN_SPI_ID; index < num_ints; index += 4) {
        gicd_write_ipriorityr(gicd_base, index, GICD_IPRIORITYR_DEF_VAL);
    }

    for (index = MIN_SPI_ID; index < num_ints; index += 16) {
        gicd_write_icfgr(gicd_base, index, 0);
    }
}

void gicv2_secure_spis_configure_props(unsigned long gicd_base, const interrupt_prop_t *interrupt_props,
                                       unsigned int interrupt_props_num)
{
    unsigned int            i;
    const interrupt_prop_t *prop_desc;

    for (i = 0; i < interrupt_props_num; i++) {
        prop_desc = &interrupt_props[i];

        if (prop_desc->intr_num < MIN_SPI_ID) {
            continue;
        }

        gicd_clr_igroupr(gicd_base, prop_desc->intr_num);

        gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, prop_desc->intr_pri);

        gicd_set_itargetsr(gicd_base, prop_desc->intr_num, gicv2_get_cpuif_id(gicd_base));

        gicd_set_icfgr(gicd_base, prop_desc->intr_num, prop_desc->intr_cfg);

        gicd_set_isenabler(gicd_base, prop_desc->intr_num);
    }
}

#if !ERROR_DEPRECATED
void gicv2_secure_spis_configure(unsigned long gicd_base, unsigned int num_ints, const unsigned int *sec_intr_list)
{
    unsigned int index, irq_num;

    for (index = 0; index < num_ints; index++) {
        irq_num = sec_intr_list[index];
        if (irq_num >= MIN_SPI_ID) {
            gicd_clr_igroupr(gicd_base, irq_num);

            gicd_set_ipriorityr(gicd_base, irq_num, GIC_HIGHEST_SEC_PRIORITY);

            gicd_set_itargetsr(gicd_base, irq_num, gicv2_get_cpuif_id(gicd_base));

            gicd_set_isenabler(gicd_base, irq_num);
        }
    }
}
#endif

void gicv2_secure_ppi_sgi_setup_props(unsigned long gicd_base, const interrupt_prop_t *interrupt_props,
                                      unsigned int interrupt_props_num)
{
    unsigned int            i;
    uint32_t                sec_ppi_sgi_mask = 0;
    const interrupt_prop_t *prop_desc;

    gicd_write_icenabler(gicd_base, 0, ~0);

    for (i = 0; i < MIN_SPI_ID; i += 4) {
        gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL);
    }
    for (i = 0; i < interrupt_props_num; i++) {
        prop_desc = &interrupt_props[i];

        if (prop_desc->intr_num >= MIN_SPI_ID) {
            continue;
        }

        if ((prop_desc->intr_num >= MIN_PPI_ID) && (prop_desc->intr_num < MIN_SPI_ID)) {
            gicd_set_icfgr(gicd_base, prop_desc->intr_num, prop_desc->intr_cfg);
        }

        sec_ppi_sgi_mask |= (1u << prop_desc->intr_num);

        gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, prop_desc->intr_pri);
    }

    gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask);

    gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask);
}

#if !ERROR_DEPRECATED
void gicv2_secure_ppi_sgi_setup(unsigned long gicd_base, unsigned int num_ints, const unsigned int *sec_intr_list)
{
    unsigned int index, irq_num, sec_ppi_sgi_mask = 0;

    gicd_write_icenabler(gicd_base, 0, ~0);

    for (index = 0; index < MIN_SPI_ID; index += 4) {
        gicd_write_ipriorityr(gicd_base, index, GICD_IPRIORITYR_DEF_VAL);
    }

    for (index = 0; index < num_ints; index++) {
        irq_num = sec_intr_list[index];
        if (irq_num < MIN_SPI_ID) {
            sec_ppi_sgi_mask |= 1U << irq_num;

            gicd_set_ipriorityr(gicd_base, irq_num, GIC_HIGHEST_SEC_PRIORITY);
        }
    }

    gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask);

    gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask);
}
#endif

#define ARM_IRQ_GPT           87
#define ARM_IRQ_SEC_TMU_O     136
#define ARM_IRQ_SEC_TMU_CH0_0 168u
#define ARM_IRQ_SEC_TMU_CH0_1 169u
#define ARM_IRQ_SEC_TMU_CH0_2 170u
#define ARM_IRQ_SEC_TMU       166u

#define PLATFORM_G1S_PROPS(grp)                                                                  \
    INTR_PROP_DESC(IRQN_TIMER0, GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_EDGE),               \
        INTR_PROP_DESC(IRQN_UART13, GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_EDGE),           \
        INTR_PROP_DESC(ARM_IRQ_SEC_TMU_CH0_2, GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_EDGE), \
        INTR_PROP_DESC(ARM_IRQ_SEC_TMU, GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_EDGE),       \
        INTR_PROP_DESC(ARM_IRQ_SEC_TMU_O, GIC_HIGHEST_SEC_PRIORITY, grp, GIC_INTR_CFG_EDGE)

#define PLATFORM_G0_PROPS(grp)

static const interrupt_prop_t rcar_interrupt_props[] = { PLATFORM_G1S_PROPS(GICV2_INTR_GROUP0),
                                                         PLATFORM_G0_PROPS(GICV2_INTR_GROUP0) };

static const struct gicv2_driver_data plat_gicv2_driver_data = {
    .gicd_base           = DEVICE_VBASE_GICD,
    .gicc_base           = DEVICE_VBASE_GICC,
    .interrupt_props     = rcar_interrupt_props,
    .interrupt_props_num = ARRAY_SIZE(rcar_interrupt_props),
};

unsigned long g_gicc_base = DEVICE_VBASE_GICC;

int gicv2_driver_init(void)
{
    gicv2_driver_data_t *driver_data = (gicv2_driver_data_t *)&plat_gicv2_driver_data;
    unsigned int         gic_version;
    unsigned int         ctlr;

    gic_version = gicd_read_pidr2(driver_data->gicd_base);
    gic_version = (gic_version >> PIDR2_ARCH_REV_SHIFT) & PIDR2_ARCH_REV_MASK;

    dprintf("gic_version = %d ; 0x02 is gicv2  0x01 is gicv1 \n", gic_version);
    dprintf("ARM GICv2 driver initialized\n");

    dprintf("***********gicv2_distif_init********** \n");

    ctlr = gicd_read_ctlr(driver_data->gicd_base);
    dprintf("read distributor ctlr = %d  ; 1 is enable group 0 , 0 is disable\n", ctlr);
    gicd_write_ctlr(driver_data->gicd_base, ctlr & ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT));

    dprintf("set gicv2  default attribute \n");
    gicv2_spis_configure_defaults(driver_data->gicd_base);

#if !ERROR_DEPRECATED
    if (driver_data->interrupt_props != NULL) {
#endif
        dprintf("set TMU interrupt  \n");
        gicv2_secure_spis_configure_props(driver_data->gicd_base, driver_data->interrupt_props,
                                          driver_data->interrupt_props_num);
#if !ERROR_DEPRECATED
    } else {
        gicv2_secure_spis_configure(driver_data->gicd_base, driver_data->g0_interrupt_num,
                                    driver_data->g0_interrupt_array);
    }
#endif

    dprintf("enable group0 \n");
    gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT);

    dprintf("******gicv2_distif_init end************ \n");
    dprintf("******gicv2 set SGI and  PPI start********* \n");

#if !ERROR_DEPRECATED
    if (driver_data->interrupt_props != NULL) {
#endif
        dprintf("gicv2 set SGI and PPI interrupt \n");
        gicv2_secure_ppi_sgi_setup_props(driver_data->gicd_base, driver_data->interrupt_props,
                                         driver_data->interrupt_props_num);
#if !ERROR_DEPRECATED
    } else {
        gicv2_secure_ppi_sgi_setup(driver_data->gicd_base, driver_data->g0_interrupt_num,
                                   driver_data->g0_interrupt_array);
    }
#endif

    ctlr = gicd_read_ctlr(driver_data->gicd_base);
    dprintf("gic ctl = %d ;if ctl =1 ,the group0 interrupt is enable \n", ctlr);

    if ((ctlr & CTLR_ENABLE_G0_BIT) == 0) {
        gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT);
    }

    unsigned int val;

    val = CTLR_ENABLE_G0_BIT | FIQ_EN_BIT | FIQ_BYP_DIS_GRP0;
    val |= IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1;

    gicc_write_pmr(driver_data->gicc_base, GIC_PRI_MASK);
    gicc_write_ctlr(driver_data->gicc_base, val);

    return 0;
}

void gicd_disable(void)
{
    uint32_t gicd_ctrl = gicd_read_ctlr(DEVICE_VBASE_GICD);
    gicd_ctrl &= ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT);
    gicd_write_ctlr(DEVICE_VBASE_GICD, gicd_ctrl);
}

void gicd_enable(void)
{
    uint32_t gicd_ctrl = gicd_read_ctlr(DEVICE_VBASE_GICD);
    gicd_ctrl |= CTLR_ENABLE_G0_BIT;
    gicd_write_ctlr(DEVICE_VBASE_GICD, gicd_ctrl);
}

void gicv2_int_disable(uint32_t irqn)
{
    gicd_set_icenabler(DEVICE_VBASE_GICD, irqn);
}

void gicv2_int_enable(uint32_t irqn)
{
    gicd_set_isenabler(DEVICE_VBASE_GICD, irqn);
}

void gicv2_int_clear_pending(uint32_t irqn)
{
    gicd_set_icpendr(DEVICE_VBASE_GICD, irqn);
}

void gicv2_int_clear_pending_active(uint32_t irqn)
{
    gicd_set_icactiver(DEVICE_VBASE_GICD, irqn);
    gicd_set_icpendr(DEVICE_VBASE_GICD, irqn);
}

void gicv2_int_config(uint32_t irqn, uint32_t trigger, uint32_t cpus, uint32_t priority, uint32_t security)
{
    if (irqn >= 16) {
        gicd_set_icfgr(DEVICE_VBASE_GICD, irqn, trigger);
    }

    if (irqn >= 32) {
        gicd_set_itargetsr(DEVICE_VBASE_GICD, irqn, cpus);
    }

    gicd_set_ipriorityr(DEVICE_VBASE_GICD, irqn, priority);

    if (security) {
        gicd_clr_igroupr(DEVICE_VBASE_GICD, irqn);
    } else {
        gicd_set_igroupr(DEVICE_VBASE_GICD, irqn);
    }
}

uint32_t gicv2_get_targetcpu(void)
{
    return gicd_get_itargetsr(DEVICE_VBASE_GICD, 0);
}

void gicv2_int_default1_config(uint32_t start, uint32_t end, uint32_t cpus)
{
    int irqn = start;

    gicv2_int_disable(irqn);
    gicv2_int_clear_pending(irqn);
    gicv2_int_config(irqn, LEVEL_TRIGGER, cpus, 0x20, SECURITY);
}

void gicv2_int_default_config(uint32_t start, uint32_t end, uint32_t cpus)
{
    int irqn;
    for (irqn = start; irqn < end; irqn++) {
        gicv2_int_disable(irqn);
        gicv2_int_clear_pending(irqn);
        gicv2_int_config(irqn, LEVEL_TRIGGER, cpus, 0x20, SECURITY);
    }
}

void gicv2_sgi_ppi_default_config(uint32_t start, uint32_t end, uint32_t priority)
{
    int irqn;
    for (irqn = start; irqn < end; irqn++) {
        gicv2_int_disable(irqn);
        gicv2_int_clear_pending(irqn);
        gicv2_int_config(irqn, LEVEL_TRIGGER, 0x0, priority, SECURITY);
    }
}

void gicd1_init(void)
{
    gicd_disable();
    gicv2_int_default1_config(54, CONFIG_IRQ_COUNT, 0x8);

    gicd_enable();
}

void gicd_init(void)
{
    gicv2_driver_init();
    dprintf("start to init gicv2\n");
    gicd_disable();
    gicv2_int_default_config(32, CONFIG_IRQ_COUNT, 0x1);
    gicv2_sgi_ppi_default_config(0, 16, 0x10);
    gicv2_sgi_ppi_default_config(16, 32, 0x20);
    gicd_enable();
}

void gicc_init(void)
{
    unsigned int val;
    val = CTLR_ENABLE_G0_BIT | FIQ_EN_BIT | FIQ_BYP_DIS_GRP0;
    val |= IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1;

    gicc_write_pmr(DEVICE_VBASE_GICC, GIC_PRI_MASK);
    gicc_write_ctlr(DEVICE_VBASE_GICC, val);
}

void gic_sgi_trigger(unsigned int id, int security, unsigned int filter, unsigned char cpulist)
{
    uint32_t value;

    assert(id < 16);

    value = filter << ICDSGIR_BIT_TLF;

    value |= cpulist << ICDSGIR_BIT_CPUTL;

    value |= id;

    if (security == 0) {
        value |= bitmask(ICDSGIR_BIT_SATT);
    }

    gicd_write_sgir(DEVICE_VBASE_GICD, value);
}

uint32_t arm_gic_acknowledge_interrupt(void)
{
    return (gicc_read_IAR(g_gicc_base) & lowbitsmask(GICC_IAR_INTID_WIDTH));
}

void arm_gic_end_of_interrupt(uint32_t cpu, uint32_t id)
{
    int val = 0;
    if (id < 16) {
        val = cpu << 10 | id;
    } else {
        val = id;
    }
    gicc_write_EOIR(g_gicc_base, val);
}
